from machine import Pin, I2C
import time

# PCA9685 默认地址
PCA9685_ADDRESS = 0x40

# 寄存器地址
MODE1 = 0x00
MODE2 = 0x01
PRE_SCALE = 0xFE

# LED0 的寄存器基地址
LED0_ON_L = 0x06
LED0_ON_H = 0x07
LED0_OFF_L = 0x08
LED0_OFF_H = 0x09

# 所有LED的ON/OFF寄存器
ALL_LED_ON_L = 0xFA
ALL_LED_ON_H = 0xFB
ALL_LED_OFF_L = 0xFC
ALL_LED_OFF_H = 0xFD

DIGITS = {
    0: [1, 1, 1, 1, 1, 1, 0],
    1: [0, 1, 1, 0, 0, 0, 0],
    2: [1, 1, 0, 1, 1, 0, 1],
    3: [1, 1, 1, 1, 0, 0, 1],
    4: [0, 1, 1, 0, 0, 1, 1],
    5: [1, 0, 1, 1, 0, 1, 1],
    6: [1, 0, 1, 1, 1, 1, 1],
    7: [1, 1, 1, 0, 0, 0, 0],
    8: [1, 1, 1, 1, 1, 1, 1],
    9: [1, 1, 1, 1, 0, 1, 1],
}


class PCA9685:
    def __init__(self, i2c, address=PCA9685_ADDRESS):
        self.i2c = i2c
        self.address = address
        self.reset()

    def reset(self):
        # 重置设备
        self.write(MODE1, 0x00)

    def write(self, reg, value):
        # 写入寄存器
        self.i2c.writeto_mem(self.address, reg, bytearray([value]))

    def read(self, reg):
        # 读取寄存器
        return self.i2c.readfrom_mem(self.address, reg, 1)[0]

    def set_pwm_freq(self, freq_hz):
        # 设置PWM频率
        prescaleval = 25000000.0  # 25MHz
        prescaleval /= 4096.0  # 12-bit
        prescaleval /= float(freq_hz)
        prescaleval -= 1.0
        prescale = int(prescaleval + 0.5)

        oldmode = self.read(MODE1)
        newmode = (oldmode & 0x7F) | 0x10  # 进入睡眠模式
        self.write(MODE1, newmode)  # 进入睡眠
        self.write(PRE_SCALE, prescale)  # 设置预分频器
        self.write(MODE1, oldmode)
        time.sleep(0.005)
        self.write(MODE1, oldmode | 0x80)  # 唤醒

    def set_pwm(self, channel, on, off):
        # 设置单个PWM通道
        self.write(LED0_ON_L + 4 * channel, on & 0xFF)
        self.write(LED0_ON_H + 4 * channel, on >> 8)
        self.write(LED0_OFF_L + 4 * channel, off & 0xFF)
        self.write(LED0_OFF_H + 4 * channel, off >> 8)

    def set_all_pwm(self, on, off):
        # 设置所有PWM通道
        self.write(ALL_LED_ON_L, on & 0xFF)
        self.write(ALL_LED_ON_H, on >> 8)
        self.write(ALL_LED_OFF_L, off & 0xFF)
        self.write(ALL_LED_OFF_H, off >> 8)

    def set_l(self, channel):
        self.set_pwm(channel, 0, 107)
        time.sleep(0.1)
        self.set_pwm(channel, 0, 0)

    def set_m(self, channel):
        self.set_pwm(channel, 0, 307)
        time.sleep(0.1)
        self.set_pwm(channel, 0, 0)

    def set_r(self, channel):
        self.set_pwm(channel, 0, 507)
        time.sleep(0.1)
        self.set_pwm(channel, 0, 0)

    def fw(self, position):
        offset = position * 7
        self.set_r(0 + offset)
        self.set_r(1 + offset)
        self.set_r(2 + offset)
        self.set_r(3 + offset)
        self.set_l(4 + offset)
        self.set_r(5 + offset)
        self.set_r(6 + offset)

    def set_num_simple_0_1(self, position):
        offset = position * 7
        self.set_r(0 + offset)
        self.set_r(3 + offset)
        self.set_l(4 + offset)
        self.set_r(5 + offset)

    def set_num_simple_1_2(self, position):
        offset = position * 7
        self.set_m(0 + offset)
        self.set_r(2 + offset)
        self.set_m(6 + offset)
        self.set_m(4 + offset)
        self.set_m(3 + offset)

    def set_num_simple_2_3(self, position):
        offset = position * 7
        self.set_m(2 + offset)
        self.set_l(4 + offset)

    def set_num_simple_3_4(self, position):
        offset = position * 7
        self.set_r(0 + offset)
        self.set_r(3 + offset)
        self.set_m(5 + offset)

    def set_num_simple_4_5(self, position):
        offset = position * 7
        self.set_m(0 + offset)
        self.set_r(1 + offset)
        self.set_m(3 + offset)

    def set_num_simple_5_6(self, position):
        offset = position * 7
        self.set_m(4 + offset)

    def set_num_simple_6_7(self, position):
        offset = position * 7
        self.set_m(1 + offset)
        self.set_r(2 + offset)
        self.set_r(3 + offset)
        self.set_l(4 + offset)
        self.set_r(5 + offset)
        self.set_r(6 + offset)
        self.set_m(2 + offset)

    def set_num_simple_7_8(self, position):
        offset = position * 7
        self.set_r(2 + offset)

        self.set_m(5 + offset)
        self.set_m(6 + offset)
        self.set_m(2 + offset)
        self.set_m(4 + offset)
        self.set_m(3 + offset)

    def set_num_simple_8_9(self, position):
        offset = position * 7
        self.set_l(4 + offset)

    def set_num_simple_9_0(self, position):
        offset = position * 7
        self.set_r(2 + offset)
        self.set_r(3 + offset)
        self.set_r(6 + offset)

        self.set_m(2 + offset)
        self.set_m(3 + offset)
        self.set_m(4 + offset)


    def set_num(self, position, num, old_num):
        if old_num is not None and old_num + 1 == num:
            # 简化方式
            if num == 0:
                self.set_num_simple_9_0(position)
            elif num == 1:
                self.set_num_simple_0_1(position)
            elif num == 2:
                self.set_num_simple_1_2(position)
            elif num == 3:
                self.set_num_simple_2_3(position)
            elif num == 4:
                self.set_num_simple_3_4(position)
            elif num == 5:
                self.set_num_simple_4_5(position)
            elif num == 6:
                self.set_num_simple_5_6(position)
            elif num == 7:
                self.set_num_simple_6_7(position)
            elif num == 8:
                self.set_num_simple_7_8(position)
            elif num == 9:
                self.set_num_simple_8_9(position)
        else:
            # 全放倒方式
            self.fw(position)
            # 必须按照顺序来，不要中间的会打架
            for i in [0, 1, 5, 6, 2, 4, 3]:
                if DIGITS.get(num)[i] == 1:
                    self.set_m(position * 7 + i)

if __name__ == '__main__':
    # 初始化I2C
    i2c = I2C(scl=Pin(5), sda=Pin(4), freq=400000)
    # 创建PCA9685实例
    pca = PCA9685(i2c)
    pca.set_pwm_freq(50)

    old_num = None
    num = 1
    while True:
        pca.set_num(0, num, old_num)
        old_num = num
        num += 1
        if num > 9:
            num = 0
        time.sleep(1)